package com.hydra.apt.router;

import com.google.auto.service.AutoService;
import com.hydra.apt.util.AptLog;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

import static com.hydra.apt.router.RouterInterceptorProcessor.ROUTER_INTERCEPTOR_ANNOTATION_CLASS_NAME;
import static com.hydra.apt.router.RouterProcessor.GENERATED_PACKAGE_NAME;
import static com.hydra.apt.router.RouterProcessor.MODULE_NAME_KEY;
import static com.hydra.apt.router.RouterProcessor.WARNING_TIPS;

/**
 * Created by Hydra.
 *
 */
@AutoService(Processor.class)
@SupportedOptions(MODULE_NAME_KEY)
@SupportedAnnotationTypes(ROUTER_INTERCEPTOR_ANNOTATION_CLASS_NAME)
public class RouterInterceptorProcessor extends AbstractProcessor {

    static final String ROUTER_INTERCEPTOR_ANNOTATION_CLASS_NAME = "com.hydra.framework.router.interceptor.RouterInterceptorAnnotation";

    private static final String LOG_TAG = RouterInterceptorProcessor.class.getSimpleName();

    private static final String GENERATED_CLASS_NAME_SUFFIX = "$JRouterInterceptor";

    private String mRouterModuleName;
    private Elements mElements;
    private Filer mFiler;
    private AptLog mLog;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        initModuleName(processingEnv);

        mElements = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
        mLog = new AptLog(processingEnv.getMessager());
    }

    private void initModuleName(ProcessingEnvironment processingEnv) {
        Map<String, String> options = processingEnv.getOptions();

        if(options == null || options.isEmpty()) {
            mLog.e(LOG_TAG, "router options is null");
            return;
        }

        mRouterModuleName = options.get(MODULE_NAME_KEY);

        if(mRouterModuleName == null || mRouterModuleName.isEmpty()) {
            mLog.e(LOG_TAG, "module name is null");
            return;
        }

        mRouterModuleName = mRouterModuleName.replaceAll("[^0-9a-zA-Z_]+", "");

        if(mRouterModuleName.isEmpty()) {
            mLog.e(LOG_TAG, "module name is null");
        }
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * TODO
     * 这里没有校验
     * 1、注解的class是否继承自RouterInterceptor
     * 2、注解的class不能为abstract
     * 暂时我觉得没啥必要
     */
    @SuppressWarnings("unchecked")
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if(annotations == null || annotations.isEmpty()) {
            mLog.i(LOG_TAG, mRouterModuleName + "annotation is null");
            return false;
        }

        try {
            Class<? extends Annotation> annotationClass = (Class<? extends Annotation>)
                    Class.forName(ROUTER_INTERCEPTOR_ANNOTATION_CLASS_NAME);

            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotationClass);

            if(elements.isEmpty()) {
                mLog.w(LOG_TAG, "no router interceptor elements");
                return false;
            }

            CodeBlock.Builder codeBlockBuilder = CodeBlock.builder();

            for(Element element : elements) {
                Annotation annotation = element.getAnnotation(annotationClass);

                Method pathMethod = annotationClass.getMethod("path");

                Object pathObject = pathMethod.invoke(annotation);

                if(pathObject == null) {
                    mLog.w(LOG_TAG, "you should add at least a path to router interceptor annotation");
                    return false;
                }

                String[] paths = (String[]) pathObject;

                if(paths.length == 0) {
                    mLog.w(LOG_TAG, "you should add at least a path to router interceptor annotation");
                    return false;
                }

                StringBuilder pathStr = new StringBuilder();

                boolean firstTime = true;
                for (Object path: paths) {
                    if (firstTime) {
                        firstTime = false;
                    } else {
                        pathStr.append(", ");
                    }
                    pathStr.append("\"" + path + "\"");
                }

                codeBlockBuilder.add("JRouter.addInterceptor($L, $L, $L);\n",
                        "new " + element.toString() + "()", annotationClass.getMethod("priority").invoke(annotation),
                        pathStr.toString());
            }

            JavaFile.builder(GENERATED_PACKAGE_NAME,
                    TypeSpec.classBuilder(mRouterModuleName + GENERATED_CLASS_NAME_SUFFIX)
                            .addJavadoc(WARNING_TIPS)
                            .addModifiers(Modifier.PUBLIC)
                            .addStaticBlock(codeBlockBuilder.build())
                            .build()).build().writeTo(mFiler);

            return true;
        } catch (Exception e) {
            mLog.e(LOG_TAG, "process error : " + e.toString());
        }

        return false;
    }
}
